package setting

import (
	"context"
	"errors"
	"gorm.io/gorm"
	"onvif_server/internal/model"
	"onvif_server/internal/repository"
	"reflect"
	"sync"
	"time"
)

var ErrorNotExists = errors.New("未找到配置")
var ErrorType = errors.New("配置值类型错误")

type SetOption func(model *sModel)

func SetName(name string) SetOption {
	return func(model *sModel) {
		model.Name = name
	}
}

func SetRemark(remark string) SetOption {
	return func(model *sModel) {
		model.Remark = remark
	}
}

func SetSort(sort int) SetOption {
	return func(model *sModel) {
		model.Sort = sort
	}
}

func SetType(typ string) SetOption {
	return func(model *sModel) {
		model.Type = typ
	}
}

type Setting struct {
	settingRepo repository.SettingRepository
	settings    sync.Map
}

func NewSetting(settingRepo repository.SettingRepository) *Setting {
	st := &Setting{
		settingRepo: settingRepo,
	}
	list, err := st.settingRepo.GetAll(context.Background())
	if err != nil {
		return st
	}
	for i := range list {
		m, _err := st.getSModel(list[i])
		if _err != nil {
			continue
		}
		st.settings.Store(list[i].Key, m)
	}

	return st
}

// Get 获取某个设置的值
func (s *Setting) Get(key string) (val any, err error) {
	m, err := s.get(key)
	if err != nil {
		return nil, err
	}
	return *m.value.Load(), nil
}

// Get 获取某个设置的值
func (s *Setting) GetWithDefault(key string, def any) any {
	m, err := s.get(key)
	if err != nil {
		return def
	}
	return *m.value.Load()
}

// Set 设置某个设置的值， 不存在会保存
func (s *Setting) Set(key string, val any) (err error) {
	m, err := s.get(key)
	if err != nil {
		return err
	}
	vT := reflect.TypeOf(val).String()
	if m.Type == "" {
		m.Type = vT
	} else {
		if vT != m.Type {
			return ErrorType
		}
	}
	v, err := toValue(val)
	if err != nil {
		return err
	}
	m.Val = v
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
	defer cancel()
	if err = s.settingRepo.Update(ctx, m.Setting); err != nil {
		return err
	}
	m.value.Store(&val)
	return
}

// Store 添加设置值，表不存在会插入，存在会更新
func (s *Setting) Store(key string, val any, setOpts ...SetOption) (err error) {
	sm, err := s.get(key)
	exists := true
	if err != nil {
		if !errors.Is(err, ErrorNotExists) {
			return err
		}
		exists = false
		sm = &sModel{
			Setting: &model.Setting{Key: key},
		}
	}

	for i := range setOpts {
		setOpts[i](sm)
	}

	vT := reflect.TypeOf(val).String()
	if sm.Type == "" {
		sm.Type = vT
	} else {
		if vT != sm.Type {
			return errors.New("配置类型设置错误")
		}
	}
	v, err := toValue(val)
	if err != nil {
		return err
	}
	sm.Val = v
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
	defer cancel()
	err = s.settingRepo.Store(ctx, sm.Setting)
	if err != nil {
		return err
	}
	sm.value.Store(&val)
	if !exists {
		s.settings.Store(key, sm)
	}
	return
}

func (s *Setting) get(key string) (val *sModel, err error) {
	if val, ok := s.settings.Load(key); ok {
		return val.(*sModel), nil
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
	defer cancel()
	sm, err := s.settingRepo.GetByKey(ctx, key)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrorNotExists
		}
		return nil, err
	}
	m, err := s.getSModel(sm)
	if err != nil {
		return nil, err
	}
	s.settings.Store(key, m)
	return m, nil
}

func (s *Setting) getSModel(sm *model.Setting) (val *sModel, err error) {
	v, err := getValue(sm.Type, sm.Val)
	if err != nil {
		return nil, err
	}
	m := &sModel{Setting: sm}
	m.value.Store(&v)
	return m, nil
}
